#pragma once

#include "outline.h"
#include "typedefs.h"
#include <vector>

namespace outline {

template <class R>
struct uniformly_sampled_parameters {
	R dt, t;
	uniformly_sampled_parameters(R r) : dt(r), t(0) { }
	R const & operator * () const { return t; }
	void operator ++ () { t += dt; }
};

template <class R>
struct bezier : typedefsR<R> 
{
	real_point_type p[4];
public:
	bezier() { }
	bezier(real_point_type const & p0, real_point_type const & p1, real_point_type const & p2, real_point_type const & p3)
		{ p[0] = p0; p[1] = p1; p[2] = p2; p[3] = p3; }
public:
	real_point_type & start() 
		{ return p[0]; }
	real_point_type const & start() const 
		{ return p[0]; }
	real_point_type & start_control() 
		{ return p[1]; }
	real_point_type const & start_control() const 
		{ return p[1]; }
	real_point_type & finish_control() 
		{ return p[2]; }
	real_point_type const & finish_control() const 
		{ return p[2]; }
	real_point_type & finish() 
		{ return p[3]; }
	real_point_type const & finish() const 
		{ return p[3]; }
	real_point_type start_d() const 
		{ return start_control() - start(); }
	real_point_type finish_d() const 
		{ return finish_control() - finish(); }
	void line(real_point_type const & start, real_point_type const & finish) {
		p[0] = start;
		p[1] = R(2.0)/3 * start + R(1.0)/3 * finish;
		p[2] = R(1.0)/3 * start + R(2.0)/3 * finish;
		p[3] = finish;
	}
	void coefficients(real_type t, real_type r[4]);
	void value_n_deriv(real_type t, real_point_type * value, real_point_type * deriv, real_type * r1 = 0, real_type * r2 = 0) const;
	real_point_type value(real_type t) const {
		real_point_type v;
		value_n_deriv(t, &v, 0);
		return v;
	}
	real_point_type deriv(real_type t) const {
		real_point_type d;
		value_n_deriv(t, 0, &d);
		return d;
	}
public:
	/// given set of points and parameters they correspond to, fits best curve;
	/// returns summed square error of fit (chi squared)
	template <class T>
	real_type best_fit(std::vector< point<T> > const & points, real_vector_type const & parameters)
		{ return best_fit_general(points, parameters.begin()); }
	template <class T>
	real_type best_fit(std::vector< point<T> > const & points, real_type dt)
		{ return best_fit_general(points, uniformly_sampled_parameters<real_type>(dt)); }
	template <class T>
	real_type best_fit_free_ends(std::vector< point<T> > const & points, real_vector_type const & parameters);
	/// filles given (pre-resized) vector with sample points from the curve delimited by equal parameter step
	/// s.front() == start(), s.back() == finish();
	/// return parameter-step between samples
	real_type sample(real_point_vector_type & s) const;
	/// much like sample(...) but writes in output vector partial lengths of polyline made from samples
	/// if normalize than each partial length will be divided on total length and
	/// partial_lengths.front() == 0, ..., partial_lengths.back() == 1
	/// if not normalize then
	/// partial_lengths.front() == 0, ..., partial_lengths.back() == total_length
	/// return parameter-step between samples
	real_type sample_lengths(real_vector_type & s, bool normalize) const;
	/// returns total length of the curve (of course, not normalized), uses given number
	/// of subdivisions to estimate it; the more subdivisions the more precise result, but slower
	real_type length(unsigned subdiv) const;
	/// replaces each normalized length along the curve in s to corresponding parameter;
	/// each value in s is from 0 to 1 and the vector is sorted (both on input and on output)
	/// the more is subdiv - the more precise will be the conversion
	void length_to_parameter(real_vector_type & s, unsigned subdiv) const;
	void parameter_to_length(real_vector_type & s, unsigned subdiv) const;
	/// simplified versions of above functions
	real_type length_to_parameter(real_type l, unsigned subdiv) const;
	real_type parameter_to_length(real_type p, unsigned subdiv) const;
private:
	/// PI - is a parameters iterator, e.g. real_vector_type.begin()
	template <class T, class PI>
	real_type best_fit_general(std::vector< point<T> > const & points, PI param_iter);
};

} //namespace outline
